package com.swak.nativex.nativeio;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.nativex.Native;
import com.swak.utils.UNSAFE;

import io.netty.util.internal.PlatformDependent;
import sun.misc.Unsafe;

/**
 * 整理重要的 native 操作
 * 
 * @author 618lf
 */
public class NativeIO {

	static Logger LOGGER = LoggerFactory.getLogger(NativeIO.class);

	public static int MADV_WILLNEED = 3;
	public static int MADV_DONTNEED = 4;
	static final Unsafe unsafe;

	static {

		/**
		 * 加载Unsafe
		 */
		unsafe = UNSAFE.getUnsafe();

		/**
		 * 加载
		 */
		Native.loadLibrary("nativeio");

		/**
		 * 初始化本地库
		 */
		try {
			initNative();
		} catch (Exception e) {
			LOGGER.error("Unable to initialize NativeIO libraries", e);
		}
	}

	/**
	 * 获得操作系统的 pageSize
	 * 
	 * @return
	 */
	public static int getOperatingSystemPageSize() {
		return unsafe.pageSize();
	}

	/**
	 * clean DirectBuffer use  Unsafe.invokeCleaner() -> buffer.clean()
	 * 
	 * @param buffer
	 * @throws IOException 
	 */
	public static void freeDirectBuffer(ByteBuffer buffer) {
		if (!buffer.isDirect()) {
			return;
		}
		PlatformDependent.freeDirectBuffer(buffer);
	}

	/**
	 * directBuffer Address.
	 * 
	 * @param buffer
	 * @return
	 */
	public static long directBufferAddress(ByteBuffer buffer) {
		if (!buffer.isDirect()) {
			return 0;
		}
		return PlatformDependent.directBufferAddress(buffer);
	}

	/**
	 * Locks the provided direct ByteBuffer into memory, preventing it from swapping
	 * out. After a buffer is locked, future accesses will not incur a page fault.
	 * 
	 * See the mlock(2) man page for more information.
	 * 
	 * @throws NativeIOException
	 */
	public static int mlock(ByteBuffer buffer, long len) throws IOException {
		if (!buffer.isDirect()) {
			throw new IOException("Cannot mlock a non-direct ByteBuffer");
		}
		try {
			mlock_native(buffer, len);
			return 1;
		} catch (Exception e) {
			return 0;
		}
	}

	/**
	 * UnLocks the provided direct ByteBuffer into memory, preventing it from swapping
	 * out. After a buffer is locked, future accesses will not incur a page fault.
	 * 
	 * See the mlock(2) man page for more information.
	 * 
	 * @throws NativeIOException
	 */
	public static int munlock(ByteBuffer buffer, long len) throws IOException {
		if (!buffer.isDirect()) {
			throw new IOException("Cannot munlock a non-direct ByteBuffer");
		}
		try {
			munlock_native(buffer, len);
			return 1;
		} catch (Exception e) {
			return 0;
		}
	}

	/**
	 * madvise the provided direct ByteBuffer into memory, preventing it from swapping
	 * out. After a buffer is locked, future accesses will not incur a page fault.
	 * 
	 * See the mlock(2) man page for more information.
	 * 
	 * @throws NativeIOException
	 */
	public static int madvise(ByteBuffer buffer, long len, int type) throws IOException {
		if (!buffer.isDirect()) {
			throw new IOException("Cannot madvise a non-direct ByteBuffer");
		}
		try {
			madvise_native(buffer, len, type);
			return 1;
		} catch (Exception e) {
			return 0;
		}
	}

	/**
	 * 初始化 native 库
	 */
	private static native void initNative();

	/**
	 * mlock_native
	 * 
	 * @param buffer
	 * @param len
	 * @throws NativeIOException
	 */
	private static native void mlock_native(ByteBuffer buffer, long len) throws NativeIOException;

	/**
	 * munlock_native
	 * 
	 * @param buffer
	 * @param len
	 * @throws NativeIOException
	 */
	private static native void munlock_native(ByteBuffer buffer, long len) throws NativeIOException;

	/**
	 * madvise_native
	 * 
	 * @param buffer
	 * @param len
	 * @param type
	 * @throws NativeIOException
	 */
	private static native void madvise_native(ByteBuffer buffer, long len, int type) throws NativeIOException;
}